#include "pch.hpp"
#include "String.hpp"

using namespace Engine::Script;




//----------------
// string Factory
//----------------

static QString StringFactory(unsigned int length, const char* s) {
  return QString::fromUtf8(s, length);
}

//----------------
// string Constructors
//----------------

static void StringDefaultConstructor(void* memory)
{
	new (memory) QString();
}

static void StringCopyConstructor(void* memory, const QString& that)
{
	new (memory) QString(that);
}

static void StringDestructor(void* memory)
{
	reinterpret_cast<QString*>(memory)->~QString();
}

static bool StringCompare(const QString& lhs, const QString& rhs)
{
	return lhs == rhs;
}

//----------------
// string = value
//----------------

static QString& AssignUIntToString(unsigned int i, QString &dest)
{
	dest = QString::number(i);
	return dest;
}

static QString& AssignIntToString(int i, QString &dest)
{
	dest = QString::number(i);
	return dest;
}

static QString& AssignFloatToString(float f, QString &dest)
{
	dest = QString::number(f);
	return dest;
}

static QString &AssignDoubleToString(double f, QString &dest)
{
	dest = QString::number(f);
	return dest;
}

//-----------------
// string += value
//-----------------

static QString &AddAssignUIntToString(unsigned int i, QString &dest)
{
	dest += QString::number(i);
	return dest;
}

static QString &AddAssignIntToString(int i, QString &dest)
{
	dest += QString::number(i);
	return dest;
}

static QString &AddAssignFloatToString(float f, QString &dest)
{
	dest += QString::number(f);
	return dest;
}

static QString &AddAssignDoubleToString(double f, QString &dest)
{
	dest += QString::number(f);
	return dest;
}

//----------------
// string + value
//----------------

static QString AddStringString(const QString& lhs, const QString& rhs)
{
	return lhs + rhs;
}

static QString AddStringUInt(const QString &str, unsigned int i)
{
	return str + QString::number(i);
}

static QString AddStringInt(const QString &str, int i)
{
	return str + QString::number(i);
}

static QString AddStringFloat(const QString &str, float f)
{
	return str + QString::number(f);
}

static QString AddStringDouble(const QString &str, double f)
{
	return str + QString::number(f);
}

//----------------
// value + string
//----------------

static QString AddIntString(int i, const QString &str)
{
	return QString::number(i) + str;
}

static QString AddUIntString(unsigned int i, const QString &str)
{
	return QString::number(i) + str;
}

static QString AddFloatString(float f, const QString &str)
{
	return QString::number(f) + str;
}

static QString AddDoubleString(double f, const QString &str)
{
	return QString::number(f) + str;
}

//----------
// string[]
//----------

/*
static char *StringCharAt(unsigned int i, QString &str)
{
	if( i >= str.size() )
	{
		// Set a script exception
		asIScriptContext *ctx = asGetActiveContext();
		ctx->SetException("Out of range");

		// Return a null pointer
		return 0;
	}

	return &str.buffer[i];
}
*/

//-----------------------
// AngelScript functions
//-----------------------





void Engine::Script::registerQString(System::ptr system)
{
	// Register the type
	system->registerObjectType("string", sizeof(QString), asOBJ_VALUE | asOBJ_APP_CLASS_CDA);

	system->registerStringFactory("string", asFUNCTION(StringFactory), asCALL_CDECL);

	// Register the object operator overloads
	// Note: We don't have to register the destructor, since the object uses reference counting
	system->registerObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()",                 asFUNCTION(StringDefaultConstructor), asCALL_CDECL_OBJLAST);
	system->registerObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &in)", asFUNCTION(StringCopyConstructor), asCALL_CDECL_OBJLAST);
	system->registerObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()",                  asFUNCTION(StringDestructor), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string &opAssign(const string &in)",              asMETHODPR(QString, operator =, (const QString&), QString&), asCALL_THISCALL);
	system->registerObjectMethod("string", "string &opAddAssign(const string &in)",           asMETHODPR(QString, operator+=, (const QString&), QString&), asCALL_THISCALL);

	system->registerObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTION(StringCompare), asCALL_CDECL_OBJFIRST);
	//system->registerObjectMethod("string", "int opCmp(const string &in) const",     asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST);
	system->registerObjectMethod("string", "string opAdd(const string &in) const", asFUNCTION(AddStringString), asCALL_CDECL_OBJFIRST);

	// Register the index operator, both as a mutator and as an inspector
	//system->registerObjectBehaviour("string", asBEHAVE_INDEX, "uint8 &f(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
	//system->registerObjectBehaviour("string", asBEHAVE_INDEX, "const uint8 &f(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);

	// Register the object methods
	system->registerObjectMethod("string", "int length() const", asMETHOD(QString,length), asCALL_THISCALL);
	system->registerObjectMethod("string", "void resize(int)", asMETHODPR(QString,resize,(int),void), asCALL_THISCALL);
	system->registerObjectMethod("string", "void reserve(int)", asMETHODPR(QString,reserve,(int),void), asCALL_THISCALL);

	// TODO: Add factory  string(const string &in str, int repeatCount)

	// TODO: Add explicit type conversion via constructor and value cast

	// TODO: Add parseInt and parseDouble. Two versions, one without parameter, one with an outparm that returns the number of characters parsed.

	// Automatic conversion from values
	system->registerObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST);
	system->registerObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST);

	system->registerObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST);
	system->registerObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST);

	system->registerObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignIntToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignIntToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string opAdd(int) const", asFUNCTION(AddStringInt), asCALL_CDECL_OBJFIRST);
	system->registerObjectMethod("string", "string opAdd_r(int) const", asFUNCTION(AddIntString), asCALL_CDECL_OBJLAST);

	system->registerObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUIntToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUIntToString), asCALL_CDECL_OBJLAST);
	system->registerObjectMethod("string", "string opAdd(uint) const", asFUNCTION(AddStringUInt), asCALL_CDECL_OBJFIRST);
	system->registerObjectMethod("string", "string opAdd_r(uint) const", asFUNCTION(AddUIntString), asCALL_CDECL_OBJLAST);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
